/*    
 * Copyright 2013 Brandon O'Toole
 * 
 * This file is part of EdgeSleuth.
 *
 * EdgeSleuth is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * EdgeSleuth is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with EdgeSleuth.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.IndieRayDev.EdgeSleuth;

import android.util.FloatMath;
import android.graphics.Bitmap;

public class EdgeDetector {
	private Bitmap original, edgeStrength, edgePixels;
	private int[] intOrig;
	private int[] intStrength;
	private int[] intEdges;
	private int threshold = 0;
	private boolean initialized = false;

	public void recycleAll(){
	}
	
	public Bitmap getOriginal(){
		return original;
	}
	
	public Bitmap getEdgeStrenths(){
		return edgeStrength;
	}

	public Bitmap getEdgePixels(){
		return edgePixels;
	}
	
	public void setThreshold(int inThresh){
		threshold = inThresh;
	}
	
	static {
		System.loadLibrary("edgedetect");
	}
	
	public EdgeDetector(Bitmap inImage) {
		int bitmapSize = inImage.getWidth()*inImage.getHeight();
		intOrig = new int[bitmapSize];
		intStrength = new int[bitmapSize];
		intEdges = new int[bitmapSize];
		
		original = inImage;
		original.getPixels(intOrig, 0, original.getWidth(), 0, 0, original.getWidth(), original.getHeight());
		
		int[] color = new int[intStrength.length];
		// native alternative
		cFindEdges(intStrength, color, intOrig, original.getHeight(), original.getWidth());
		edgeStrength = intToBitmap(color);
		//findEdges();

		thresholdEdges();
		
		initialized = true;
	}
	
	public native void cFindEdges(int[] strength, int[] color, int[] original, int o_h, int o_w);
	public native void loopCompute(int[] inArray, int[] outArray);
	public void findEdges(){
		int oWidth = original.getWidth();
		int oLengthEnd = oWidth * (original.getHeight()-10);
		for( int i=10*oWidth; i<oLengthEnd; i++){
			intStrength[i] = calculateEdgeStrength(i);
			// To debug the native method, call the sub-function here
				//intStrength[index] = cCalculateEdgeStrength(index);
		}
		
		for( int i=0; i<original.getWidth(); i++){
			for( int k=0; k<10; k++){
				intStrength[i+k*original.getWidth()] = 0;
				intStrength[i+(original.getHeight()-1) - k] = 0;
			}
		}
		for( int j=0; j<original.getHeight(); j++){
			for( int k=0; k<10; k++){
				intStrength[j*original.getWidth() + k] = 0;
				intStrength[(j+1)*original.getWidth() - 1 - k] = 0;
			}
		}
		
		int[] color = new int[intStrength.length];
		
		// replaced with jni call
		loopCompute(intStrength, color);
		/*for(int i=0; i<intStrength.length; i++){
			color[i] = cComputeColor(intStrength[i]);
		}*/
		
		edgeStrength = intToBitmap(color);
		
	}
	
	public void thresholdEdges(){
		if(!initialized){
			return;
		}
		for( int i=0; i<(intStrength.length); i++){
			if(intStrength[i] < threshold){
				intEdges[i] = 0xff000000;
			} else{
				intEdges[i] = 0xffffffff;
			}
		}
		
		edgePixels = intToBitmap(intEdges);
	}
	
	public native int cCalculateEdgeStrength(int index, int[] o, int o_h, int o_w);
	public int calculateEdgeStrength(int index){
		float dVInten, dHInten; 
		int nColor, sColor, eColor, wColor;
		int r = 1;
		
		nColor = intOrig[index - r * original.getWidth()];
		sColor = intOrig[index + r * original.getWidth()];
		eColor = intOrig[index + r]; 
		wColor = intOrig[index - r];
		
		dVInten = computeDIntensity(nColor, sColor);
		dHInten = computeDIntensity(eColor, wColor);
	
		return (int)FloatMath.sqrt((dVInten*dVInten) + (dHInten*dHInten));	
	}
	
	private Bitmap intToBitmap(int[] inArray){
		Bitmap bitmap;
		
		bitmap = Bitmap.createBitmap(inArray, original.getWidth(), original.getHeight(), Bitmap.Config.ARGB_8888);
		
		return bitmap;
	}
	
	/*
	private int computeIntensity(int color){
		float powSum;
		
		int r = (color >> 16) & 0xFF;
		int g = (color >> 8) & 0xFF;
		int b = color & 0xFF;
		
		powSum = (float)(Math.pow((double)r, 2) + Math.pow((double)g, 2) + Math.pow((double)b, 2));
		return (int)FloatMath.sqrt(powSum);
	}
	*/
	
	public native float cComputeDIntensity(int c, int c0);
	private float computeDIntensity(int color, int color0){
		int dR, dG, dB;
		
		dR = ( (color>>16) & 0xff ) - ( (color0>>16) & 0xff );
		dG = ( (color>>8) & 0xff )  - ( (color0>>8) & 0xff );
		dB = ( (color) & 0xff )     - ( (color0) & 0xff );
		
		return FloatMath.sqrt(   dR*dR + dG*dG + dB*dB   );
	}
	
	public native int cComputeColor(int inten);
	private int computeColor(int intensity){
		int color;
		
		color = intensity & 0xff;
		color += (intensity & 0xff) << 8;
		color += (intensity & 0xff) << 16;
		color += color | 0xff000000;
		
		return color;
	}

}
